﻿module KujibikiModule
open System
open System.Xml.Linq
open CSharpLib

type PrizeType = 
    | Camera
    | IPhone
    | Hazure
    member this.GetPrize() =
        match this with
        | Camera -> new Prize("カメラ", 30000)
        | IPhone -> new Prize("iPhone", 80000)
        | _ -> new Prize("ハズレwwwwww", -3000)
        
let GetPrize () = 
    let random = new Random()
    let InRange s v e = v >= s && v <= e
    let (|IsCamera|_|) (n : int) = if InRange 1 n 50 then Some() else None
    let (|IsiPhone|_|) (n : int) = if InRange 51 n 90 then Some() else None
    let (|IsHazure|_|) (n : int) = if InRange 91 n 100 then Some() else None
    let NextPrize (cur : Option<PrizeType>) (last : Option<PrizeType>)  = 
        if (Option.isSome last) && last.Value <> cur.Value then cur else Some(Hazure)

    let foldFunc acc item =
        let currentPrize = 
            match item with
            | IsCamera -> Some(Camera)
            | IsiPhone -> Some(IPhone)
            | _ -> Some(Hazure)
        NextPrize currentPrize acc

    let foldRet = List.fold foldFunc None [random.Next(1,100);random.Next(1,100);random.Next(1,100)]
    foldRet.Value.GetPrize()


type Range = {Min : int; Max : int}
type DescriptionAndValue = {Id : int; Description : string; Value : int}
type PrizesInfo = {Genre : string; Range : Range; Prize : DescriptionAndValue seq }
let GetPrizeFromXml () =
    (*
        ジャンル決めに1回目のランダムチャンスを実施 → PrizeSetが決定する   ※ランダム範囲はPrizeSetsノードのmin属性値～max属性値
        景品決めに2回目のランダムチャンスを実施 → Prizeが決まる ※ランダム範囲は1～Prizesノードの子ノード数
    *)

    //Memo: Linqが使えない。次のバージョンから使用できるみたいだ。
    let xdoc = 
        new XDocument(
            //Memo: エレメント名を暗黙的に変換してくれないようなので、XName.Get()で明示的に指定する必要あり。
            new XElement(XName.Get("Kujibiki"), 
                new XElement(XName.Get("PrizeSets"), new XAttribute(XName.Get("min"), 1), new XAttribute(XName.Get("max"), 100),
                    new XElement(XName.Get("PrizeSet"), new XAttribute(XName.Get("genre"), "野菜"),
                        new XElement(XName.Get("Range"), new XAttribute(XName.Get("min"), 1), new XAttribute(XName.Get("max"), 30)),
                        new XElement(XName.Get("Prizes"),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 0), new XAttribute(XName.Get("description"), "トマト"), new XAttribute(XName.Get("value"), 100)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 1), new XAttribute(XName.Get("description"), "なすび"), new XAttribute(XName.Get("value"), 120)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 2), new XAttribute(XName.Get("description"), "とうもろこし"), new XAttribute(XName.Get("value"), 140)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 3), new XAttribute(XName.Get("description"), "きゅうり"), new XAttribute(XName.Get("value"), 160)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 4), new XAttribute(XName.Get("description"), "キャベツ"), new XAttribute(XName.Get("value"), 180)))),
                    new XElement(XName.Get("PrizeSet"), new XAttribute(XName.Get("genre"), "文房具"),
                        new XElement(XName.Get("Range"), new XAttribute(XName.Get("min"), 31), new XAttribute(XName.Get("max"), 74)),
                        new XElement(XName.Get("Prizes"),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 0), new XAttribute(XName.Get("description"), "鉛筆(H)"), new XAttribute(XName.Get("value"), 50)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 1), new XAttribute(XName.Get("description"), "鉛筆(B)"), new XAttribute(XName.Get("value"), 60)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 2), new XAttribute(XName.Get("description"), "消しゴム"), new XAttribute(XName.Get("value"), 100)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 3), new XAttribute(XName.Get("description"), "定規"), new XAttribute(XName.Get("value"), 200)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 4), new XAttribute(XName.Get("description"), "ハサミ"), new XAttribute(XName.Get("value"), 550)))),
                    new XElement(XName.Get("PrizeSet"), new XAttribute(XName.Get("genre"), "レア"),
                        new XElement(XName.Get("Range"), new XAttribute(XName.Get("min"), 75), new XAttribute(XName.Get("max"), 80)),
                        new XElement(XName.Get("Prizes"),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 0), new XAttribute(XName.Get("description"), "レアA"), new XAttribute(XName.Get("value"), 50000)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 1), new XAttribute(XName.Get("description"), "レアB"), new XAttribute(XName.Get("value"), 60000)))),
                    new XElement(XName.Get("PrizeSet"), new XAttribute(XName.Get("genre"), "家電"),
                        new XElement(XName.Get("Range"), new XAttribute(XName.Get("min"), 81), new XAttribute(XName.Get("max"), 100)),
                        new XElement(XName.Get("Prizes"),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 0), new XAttribute(XName.Get("description"), "洗濯機"), new XAttribute(XName.Get("value"), 1200)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 1), new XAttribute(XName.Get("description"), "テレビ"), new XAttribute(XName.Get("value"), 6000)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 2), new XAttribute(XName.Get("description"), "掃除機"), new XAttribute(XName.Get("value"), 3000)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 3), new XAttribute(XName.Get("description"), "電話"), new XAttribute(XName.Get("value"), 20000)),
                            new XElement(XName.Get("Prize"), new XAttribute(XName.Get("id"), 4), new XAttribute(XName.Get("description"), "パソコン"), new XAttribute(XName.Get("value"), 4500)))))))

    let rootEle = xdoc.Root
    
    let GetRange () = 
        let ps = rootEle.Element(XName.Get("PrizeSets"))
        ((int (ps.Attribute(XName.Get("min")).Value)), (int (ps.Attribute(XName.Get("max")).Value)))

    let random = new Random()
    let pSetVal = random.Next(fst (GetRange()), snd (GetRange()))



    let GetAllPrizeInfo() = 
        let sets = Seq.toArray (rootEle.Element(XName.Get("PrizeSets")).Elements())
        //Todo: このシーケンスは機能しているだろうか。
        seq {
            //Todo: 配列要素へのアクセス方法がイマイチ。そもそもArrayの使用方法が間違ってると思われる。
            for i = 1 to Array.length sets do
                let set = sets.[i-1]
                let range = set.Element(XName.Get("Range"))
                let prize = Seq.toArray (set.Element(XName.Get("Prizes")).Elements(XName.Get("Prize")))
                //Todo: このシーケンスは機能しているだろうか。
                let prizeSq = seq {
                    for j = 1 to Array.length prize do
                        let id = int (prize.[j-1].Attribute(XName.Get("id")).Value)
                        let desc = prize.[j-1].Attribute(XName.Get("description")).Value
                        let value = int (prize.[j-1].Attribute(XName.Get("value")).Value)
                        yield { Id = id; Description = desc; Value = value }   
                }
                
                yield { 
                    Genre = set.Attribute(XName.Get("genre")).Value;
                    Range = {Min = (int (range.Attribute(XName.Get("min")).Value)); Max = (int (range.Attribute(XName.Get("max")).Value))};
                    Prize = prizeSq
                }
        }

    let prizeInfo =
        GetAllPrizeInfo() 
        |> Seq.find (fun pi ->  pSetVal >= pi.Range.Min && pSetVal <= pi.Range.Max)
    let pVal = random.Next(0, (Seq.length prizeInfo.Prize) - 1)
    let dav = prizeInfo.Prize |> Seq.find (fun pi -> pi.Id = pVal)
    new Prize(dav.Description, dav.Value)